﻿using System;
using System.Linq.Expressions;
using System.Reflection;

using BrightSword.Feber.Core;

namespace BrightSword.Feber.Samples
{
    /// <summary>
    /// Implementation of Object PrettyPrinters using the Feber approach of creating type-memoized object functions and applying them on demand.
    /// 
    /// This is an example of using the Feber approach to create action methods to operate on a single objects of type T
    /// </summary>
    public static class PrettyPrinter
    {
        /// <summary>
        ///     An extension method to print out all scalar properties from <paramref name="_this" /> onto the console
        /// </summary>
        /// <param name="_this"> The object to be printed </param>
        /// <typeparam name="T"> </typeparam>
        public static void Print<T>(this T _this)
        {
            PrettyPrinter<T>.Print(_this);
        }
    }

    public static class PrettyPrinter<TProto>
    {
        private static readonly PrettyPrinterBuilder _builder = new PrettyPrinterBuilder();

        public static void Print(TProto instance)
        {
            _builder.Action(instance);
        }

        private class PrettyPrinterBuilder : ActionBuilder<TProto, TProto>
        {
            protected override Expression PropertyExpression(PropertyInfo property,
                                                             ParameterExpression instanceParameterExpression)
            {
                var propertyExpression = Expression.Property(instanceParameterExpression,
                                                             property);
                if (property.PropertyType == typeof (string))
                {
                    return Expression.Call(typeof (Console),
                                           "WriteLine",
                                           null,
                                           Expression.Constant("\t{0} : {1}",
                                                               typeof (string)),
                                           Expression.Constant(property.Name,
                                                               typeof (string)),
                                           propertyExpression);
                }

                return Expression.Call(typeof (Console),
                                       "WriteLine",
                                       null,
                                       Expression.Constant("\t{0} : {1}",
                                                           typeof (string)),
                                       Expression.Constant(property.Name,
                                                           typeof (string)),
                                       Expression.Call(typeof (Convert),
                                                       "ToString",
                                                       null,
                                                       propertyExpression));
            }
        }
    }
}